package com.ibatis4J.framework.dal.dao;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.SqlParameter;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.object.GenericStoredProcedure;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;

import com.ibatis4J.framework.dal.dao.support.rowmapper.RowMapperFactory;
import com.ibatis4J.framework.dal.dao.support.sql.FreeMakerParser;
import com.ibatis4J.framework.dal.dao.support.sql.SqlParser;
import com.ibatis4J.framework.dal.dao.support.sql.SqlParserManager;
import com.ibatis4J.framework.dal.dao.support.value.ValueParser;
import com.ibatis4J.framework.dal.resource.XmlResource;
import com.ibatis4J.framework.dal.resource.parse.SqlBean;
import com.ibatis4J.framework.dal.util.DalUtils;

/**
 * 数据操作层，包装了数据层面的基本操作方法
 * 
 * @author liuwenya
 *
 */
public class DataBaseOperation extends XmlResource {

	/** 日志 */
	private static final Logger LOGGER = LoggerFactory.getLogger(DataBaseOperation.class);

	/** SQL失效时间 */
	private static final Long SQLTIMEOUT = 100L;
	
	/**
	 * 数据持久化操作
	 * 
	 * @param entity  数据实体
	 * @return  持久化操作成功记录数
	 */
	public Number persist(Object entity){
		return persist(entity,Number.class);
	}
	
	/**
	 * 数据持久化
	 * 
	 * @param entity  数据实体
	 * @param requiredType  需处理的类型
	 * @param <T> 泛型对象
	 * @return  持久化操作成功记录数
	 */
	public <T> T persist(Object entity,Class<T> requiredType){
		SqlParser sqlParser=SqlParserManager.getSqlParser(entity.getClass());
		String insertSQL=sqlParser.getInsert();
		Map<String,Object> paramMap=ValueParser.parser(entity);
		/** SQL模板渲染 */
        insertSQL = FreeMakerParser.process(insertSQL, paramMap);
        KeyHolder keyHolder = new GeneratedKeyHolder();
        logMessage("persist", insertSQL, paramMap);
        long beginDate = System.currentTimeMillis();
        /** 渲染后获取JDBC模板 */
        getJdbcTemplate().update(insertSQL,
                new MapSqlParameterSource(paramMap), keyHolder, new String[] { sqlParser.getIdName() });
        Object key = paramMap.get(sqlParser.getId());
        if (key == null || (key instanceof Number && ((Number) key).doubleValue() == 0.0d)) {
            return (T) keyHolder.getKey();
        }
        logMessage("persist", insertSQL, paramMap, System.currentTimeMillis() - beginDate);
        return (T) key;
	}

	/**
	 * 数据合并与更新
	 * 
	 * @param entity 更新的数据实体
	 * @return 数据更新后的结果
	 */
	public int merge(Object entity){
		String updateSql=SqlParserManager.getSqlParser(entity.getClass()).getUpdate();
		Map<String,Object> paramMap=ValueParser.parser(entity);
		/** FreeMarker模板渲染 */
		updateSql = FreeMakerParser.process(updateSql, paramMap);
		
		logMessage("merge", updateSql, paramMap);
        long beginDate = System.currentTimeMillis();
        /** 调用JDBCTemplate实现更新，返回更新成功的记录数 */
        int result = getJdbcTemplate().update(
                updateSql, paramMap);
        logMessage("merge", updateSql, paramMap, System.currentTimeMillis() - beginDate);

        return result;
	}
	
	/**
     * 动态更新
     * 
     * @param entity 更新的数据实体
     * @return 返回更新的记录数目
     */
	public int dynamicMerge(Object entity){
		Map<String,Object> paramMap=ValueParser.parser(entity);
		String updateSql=SqlParserManager.getSqlParser(entity.getClass()).getDynamicUpdate(paramMap);
		/** FreeMarker模板渲染 */
		updateSql = FreeMakerParser.process(updateSql, paramMap);
		
		logMessage("dynamic", updateSql, paramMap);
        long beginDate = System.currentTimeMillis();
        /** 调用JDBCTemplate实现更新，返回更新成功的记录数 */
        int result = getJdbcTemplate().update(
                updateSql, paramMap);
        logMessage("dynamic", updateSql, paramMap, System.currentTimeMillis() - beginDate);

        return result;
	}
	
	 /**
     * 单表删除操作 根据主键删除记录
     * 
     * @param entity 实体对象
     * @return 删除成果的记录数
     */
	public int remove(Object entity){
		String removeSql = SqlParserManager.getSqlParser(entity.getClass()).getDelete();
        Map<String, Object> paramMap = ValueParser.parser(entity);
        /** FreeMarker模板渲染 */
        removeSql = FreeMakerParser.process(removeSql, paramMap);
        logMessage("remove", removeSql, paramMap);
        long beginDate = System.currentTimeMillis();
        /** 调用JDBCTemplate实现更新，返回更新成功的记录数 */
        int result = getJdbcTemplate().update(
                removeSql, paramMap);
        logMessage("remove", removeSql, paramMap, System.currentTimeMillis() - beginDate);

        return result;
	}
	
	/**
     * 查询单个记录 根据传入实体类的主键
     * 
     * @param entityClass 实体类
     * @param entity 查询对象
     * @param <T> 泛型对象
     * @return 查询结果
     */
    public <T> T find(Class<T> entityClass, Object entity) {
        String findSql = SqlParserManager.getSqlParser(entity.getClass()).getSelect();
        Map<String, Object> paramMap = ValueParser.parser(entity);
        /** FreeMarker模板渲染 */
        findSql = FreeMakerParser.process(findSql, paramMap);
        logMessage("find", findSql, paramMap);
        long beginDate = System.currentTimeMillis();
        /** 调用JDBCTemplate实现单记录查询，并返回查询结果 */
        List<T> resultList = getJdbcTemplate().query(findSql, paramMap,
                new RowMapperFactory<T>(entityClass).getRowMapper());
        logMessage("find", findSql, paramMap, System.currentTimeMillis() - beginDate);

        return singleResult(resultList);
    }
	
	 /**
	  * 根据sqlId查询单条记录
	  * 
	  * @param sqlId  SQLID
	  * @param paramMap   查询参数
	  * @param requiredType  返回对象类型
	  * @return   查询结果
	  */
	 public <T> T queryForObject(String sqlId,Map<String,Object> paramMap,Class<T> requiredType){
		 return this.queryForObject(sqlId, paramMap, new RowMapperFactory<T>(requiredType).getRowMapper());
	 }
	 
	 /**
	  * 根据sqlId查询单条记录
	  * 
	  * @param sqlId  SQLID
	  * @param param   查询参数对象
	  * @param requiredType  返回对象类型
	  * @return   查询结果
	  */
	 public <T> T queryForObject(String sqlId,Object param,Class<T> requiredType){
		 return this.queryForObject(sqlId, DalUtils.convertToMap(param), new RowMapperFactory<T>(requiredType).getRowMapper());
	 }
	
	 /**
	  * 根据sqlId查询单条记录
	  * 
	  * @param sqlId SQLID
	  * @param paramMap  查询参数
	  * @param rowMapper  返回结果对象映射实例
	  * @return  查询结果
	  */
	public <T> T queryForObject(String sqlId, Map<String, Object> paramMap, RowMapper<T> rowMapper) {
		SqlBean sqlBean = getSQL(sqlId);
		/** Freemarker模板渲染 */
		String sql = FreeMakerParser.process(paramMap, sqlBean.getContent(), sqlId);
		logMessage("queryForObject",sql,paramMap);
		long beginDate = System.currentTimeMillis();
		/** 调用JDBCTemplate实现查询，并返回查询结果 */
		List<T> resultList = getJdbcTemplate().query(sql, paramMap, rowMapper);
		logMessage("queryForObject",sql,paramMap,System.currentTimeMillis()-beginDate);
		return singleResult(resultList);
	}
	
	/**
	 * 根据sqlId查询单条记录
	 * 
	 * @param sqlId   SQLID
	 * @param param   查询参数对象
	 * @param rowMapper  返回结果对象映射实例
	 * @return  查询结果
	 */
	public <T> T queryForObject(String sqlId,Object param,RowMapper<T> rowMapper){
		 return this.queryForObject(sqlId, DalUtils.convertToMap(param), rowMapper);
	 }
	
	/**
	 * 查询并返回映射集
	 * 
	 * @param sqlId  SQLID
	 * @param paramMap  查询参数
	 * @return  查询结果
	 */
	public Map<String,Object> queryForMap(String sqlId,Map<String,Object> paramMap){
		SqlBean sqlBean = getSQL(sqlId);
		/** Freemarker模板渲染 */
		String sql = FreeMakerParser.process(paramMap, sqlBean.getContent(), sqlId);
		logMessage("queryForMap",sql,paramMap);
		long beginDate = System.currentTimeMillis();
		/** 调用JDBCTemplate实现查询，并返回查询结果 */
		Map<String,Object> map=singleResult(getJdbcTemplate().queryForList(sql, paramMap));
		logMessage("queryForMap",sql,paramMap,System.currentTimeMillis()-beginDate);
		return map;
	}
	
	/**
	 * 查询并返回映射集
	 * 
	 * @param sqlId  SQLID
	 * @param param  查询参数对象
	 * @return       查询结果
	 */
	public Map<String,Object> queryForMap(String sqlId,Object param){
		return this.queryForMap(sqlId, DalUtils.convertToMap(param));
	}
	
	/**
	 * 根据sqlId查询多条记录，返回list型结果集
	 * 
	 * @param sqlId   SQLID
	 * @param paramMap  查询参数
	 * @param rowMapper  返回结果对象映射实例
	 * @return   查询结果
	 */
	public <T> List<T> queryForList(String sqlId, Map<String, Object> paramMap, RowMapper<T> rowMapper) {
		SqlBean sqlBean = getSQL(sqlId);
		/** Freemarker模板渲染 */
		String sql = FreeMakerParser.process(paramMap, sqlBean.getContent(), sqlId);
		logMessage("queryForList (3 paramter)",sql,paramMap);
		long beginDate = System.currentTimeMillis();
		/** 调用JDBCTemplate实现查询，并返回查询结果 */
		List<T> list=getJdbcTemplate().query(sql, DalUtils.mapIfNull(paramMap), rowMapper);
		logMessage("queryForList (3 paramter)",sql,paramMap,System.currentTimeMillis()-beginDate);
		return list;
	}
	
	/**
	 * 根据sqlId查询多条记录，返回list型结果集
	 * 
	 * @param sqlId   SQLID
	 * @param paramMap  查询参数
	 * @param requiredType 返回对象类型
	 * @return   查询结果
	 */
	public <T> List<T> queryForList(String sqlId, Map<String, Object> paramMap, Class<T> requiredType) {
		return this.queryForList(sqlId, paramMap, new RowMapperFactory<T>(requiredType).getRowMapper());
	}
	
	/**
	 * 根据sqlId查询多条记录，返回list型结果集
	 * 
	 * @param sqlId   SQLID
	 * @param param   查询参数对象
	 * @param requiredType  返回对象类型
	 * @return   查询结果
	 */
	public <T> List<T> queryForList(String sqlId, Object param, Class<T> requiredType) {
		return this.queryForList(sqlId, DalUtils.convertToMap(param), new RowMapperFactory<T>(requiredType).getRowMapper());
	}

	/**
	 * 根据sqlId查询多条记录，返回list型结果集
	 * 
	 * @param sqlId   SQLID
	 * @param param   查询参数对象
	 * @param rowMapper   返回结果对象映射实例
	 * @return  查询结果
	 */
	public <T> List<T> queryForList(String sqlId, Object param,  RowMapper<T> rowMapper) {
		return this.queryForList(sqlId, DalUtils.convertToMap(param), rowMapper);
	}
	
	/**
	 * 根据sqlId查询多条记录，返回List<Map<String, Object>>型结果集，queryForList重载方法
	 * 
	 * @param sqlId  SQLID
	 * @param paramMap   查询参数
	 * @return         查询结果
	 */
	public List<Map<String,Object>> queryForList(String sqlId, Map<String, Object> paramMap){
		SqlBean sqlBean = getSQL(sqlId);
		/** Freemarker模板渲染 */
		String sql = FreeMakerParser.process(paramMap, sqlBean.getContent(), sqlId);
		logMessage("queryForList (2 paramter)",sql,paramMap);
		long beginDate = System.currentTimeMillis();
		/** 调用JDBCTemplate实现查询，并返回查询结果 */
		List<Map<String,Object>> list=getJdbcTemplate().queryForList(sql, DalUtils.mapIfNull(paramMap));
		logMessage("queryForList (2 paramter)",sql,paramMap,System.currentTimeMillis()-beginDate);
		return list;
	}
	
	
	/**
	 * queryForList重载方法
	 * 
	 * @param sqlId  SQLID
	 * @param param  查询参数对象
	 * @return       查询结果
	 */
	public List<Map<String,Object>> queryForList(String sqlId, Object param){
		return this.queryForList(sqlId, DalUtils.convertToMap(param));
	}
	
	/**
	 * 执行sql,返回更新成功的记录数
	 * 
	 * @param sqlId  SQLID
	 * @param paramMap  查询参数
	 * @return  更新成功的记录数
	 */
	public int execute(String sqlId ,Map<String, Object> paramMap){
		SqlBean sqlBean = getSQL(sqlId);
		/** Freemarker模板渲染 */
		String sql = FreeMakerParser.process(paramMap, sqlBean.getContent(), sqlId);
		logMessage("execute",sql,paramMap);
		long beginDate = System.currentTimeMillis();
		/** 调用JDBCTemplate实现更新，并返回更新成功的记录数 */
		int result=getJdbcTemplate().update(sql, DalUtils.mapIfNull(paramMap));
		logMessage("execute",sql,paramMap,System.currentTimeMillis()-beginDate);
		return result;
	}
	
	/**
	 * 执行sql,返回更新成功的记录数
	 * 
	 * @param sqlId  SQLID
	 * @param param  查询参数对象
	 * @return       更新成功的记录数
	 */
	public int execute(String sqlId, Object param){
		return this.execute(sqlId, DalUtils.convertToMap(param));
	}
	

    /**
     * 批量更新
     * 
     * @param sqlId SQLID
     * @param batchValues 需要批处理的集合
     * @return 批处理成功记录数
     */
	public int [] batchUpdate(String sqlId,Map<String, Object> []batchValues){
		 Map<String, Object> paramMap = new HashMap<String, Object>();
		 if(batchValues!=null && batchValues[0]!=null){
			 paramMap=batchValues[0];
		 }
		/** 组装SqlBean */
		SqlBean sqlBean = getSQL(sqlId);
		/** Freemarker模板渲染 */
		String sql = FreeMakerParser.process(paramMap, sqlBean.getContent(), sqlId);
		logMessage("batchUpdate", sql, String.valueOf(batchValues == null ? 0 : batchValues.length));
		long beginDate = System.currentTimeMillis();
		int[] result;
		/** 调用JDBCTemplate批量更新，返回更新成功的记录数 */
		result = getJdbcTemplate().batchUpdate(sql, batchValues);
		logMessage("batchUpdate", sql, String.valueOf(batchValues == null ? 0 : batchValues.length),
				System.currentTimeMillis() - beginDate);

		return result;
	}
	
	/**
     * 调存储过程
     * 
     * @param sqlId SQLID
     * @param paramMap 执行参数
     * @param sqlParameters sqlcommand参数的对象
     * @return 存储过程执行结果
     */
    public Map<String, Object> call(String sqlId, Map<String, Object> paramMap, List<SqlParameter> sqlParameters) {
        Map<String, Object> paramMapTmp = DalUtils.mapIfNull(paramMap);
        SqlBean sqlBean = getSQL(sqlId);
        /** SQL渲染 */
        String sql = FreeMakerParser.process(paramMapTmp, sqlBean.getContent(), sqlId);
        logMessage("call", sql, paramMapTmp);
        long beginDate = System.currentTimeMillis();
        /** 调用存储过程 */
        GenericStoredProcedure storedProcedure = new GenericStoredProcedure();
        /** 放入数据源 */
        storedProcedure.setDataSource(getDefaultDataSource());
        /** 放入SQL */
        storedProcedure.setSql(sqlBean.getContent());
        for (SqlParameter sqlParameter : sqlParameters) {
            storedProcedure.declareParameter(sqlParameter);
        }
        logMessage("call", sql, paramMapTmp, System.currentTimeMillis() - beginDate);
        return storedProcedure.execute(paramMapTmp);
    }
    
	/**
     * 打印sql的执行信息
     * 
     * @param method 方法名
     * @param sql SQL串
     * @param object 对象
     */
    private void logMessage(String method, String sql, Object object) {
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug(method + " method SQL: [" + sql + "]");
            LOGGER.debug(method + " method parameter:" + object);
        }
    }
    
	/**
	 * 打印超时SQL的执行时间
	 * @param method   方法名
	 * @param sql      SQL串
	 * @param object   对象
	 * @param executeTime  执行时间
	 */
	private void logMessage(String method, String sql, Object object, long executeTime) {
		
		if (executeTime >= SQLTIMEOUT) {
			if (LOGGER.isDebugEnabled()) {
				LOGGER.error(method + " method executeTime:" + executeTime + "ms");
			}
		}
	}

	/**
	 * 返回结果集中的第一条记录
	 * @param resultList  结果集
	 * @param  <T>   泛型对象
	 * @return   结果集中的第一条记录
	 */
	private <T> T singleResult(List<T> resultList){
		if(resultList!=null){
			int size=resultList.size();
			if(size>0){
				if(LOGGER.isDebugEnabled() && size>1){
					 LOGGER.debug("Incorrect result size: expected " + 1 + ", actual " + size
	                            + " return the first element.");
				}
				return resultList.get(0);
			}
			if(size==0){
				if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("Incorrect result size: expected " + 1 + ", actual " + size);
                }
                return null;
			}
		}
		return null;
	}
	/**
	 * 返回JDBC模板
	 * 
	 * @return JDBC模板
	 */
	protected NamedParameterJdbcTemplate getJdbcTemplate() {
		return new NamedParameterJdbcTemplate(getDefaultDataSource());
	}


}
